import { isNullOrUnDef } from '@/utils/utils'
export type SimpleStorageType = 'session' | 'local';

export interface StoreData {
  value: object
  time: number
  expire: number | null
}

export interface SimpleStorageItem {
  key: string
  value: unknown
}

export interface StringDictionary {
  [key: string]: string
}

/** Fallback storage provider for environments where the Storage API isn't available */
class AltStorage {
  private data: StringDictionary = {};

  getItem(key: string) {
    return this.data[key] || null
  }

  setItem(key: string, value: string) {
    this.data[key] = value
  }

  removeItem(key: string) {
    delete this.data[key]
  }

  clear() {
    this.data = {}
  }

  getData() {
    return this.data
  }

  key(index: number) {
    return Object.keys(this.data)[index]
  }

  get length() {
    return Object.keys(this.data).length
  }
}

export class SimpleStorage {
  private storageSource!:
    | WindowLocalStorage['localStorage']
    | WindowSessionStorage['sessionStorage']
    | AltStorage;

  constructor(storageType: SimpleStorageType) {
    if (!this.isLocalAndSessionStorageSupported()) {
      this.storageSource = new AltStorage()
      return
    }

    if (storageType === 'session') {
      this.storageSource = window.sessionStorage
      return
    }

    if (storageType === 'local') {
      this.storageSource = window.localStorage
    }
  }

  setItem(key: string, rawValue: any, expire: number | null) {
    const stringData = JSON.stringify({
      value: rawValue,
      time: Date.now(),
      expire: !isNullOrUnDef(expire) ? new Date().getTime() + expire * 1000 : null,
    });
    
    this.storageSource.setItem(key, stringData)
  }

  getItem(key: string) {
    const data = this.storageSource.getItem(key)

    if (typeof data !== 'string') {
      return data
    }

    try {
      const { value, expire } = JSON.parse(data)
      if (isNullOrUnDef(expire) || expire >= new Date().getTime()) {
        return value
      }
      this.removeItem(key);
    } catch (error) {
      return data
    }
  }

  removeItem(key: string) {
    return this.storageSource.removeItem(key)
  }

  removeAll() {
    return window.sessionStorage.clear()
  }

  /** Remove all items from storage */
  clear() {
    return this.storageSource.clear()
  }

  get length() {
    return this.storageSource.length
  }

  getAllItems() {
    const items: SimpleStorageItem[] = []

    for (let i = this.length - 1; i >= 0; i--) {
      const key = this.storageSource.key(i)

      if (key !== null) {
        const value = this.getItem(key)
        items.push({ key, value })
      }
    }

    return items
  }

  getAllItemsAsync(): Promise<SimpleStorageItem[]> {
    return new Promise(resolve =>
      setTimeout(() => resolve(this.getAllItems()))
    )
  }

  private isLocalAndSessionStorageSupported(): boolean {
    const key = '_simple-storage_test-key'
    try {
      if (
        typeof window === 'undefined' ||
        !window.sessionStorage ||
        !window.localStorage
      ) {
        return false
      }
      window.sessionStorage.setItem(key, '1')
      window.sessionStorage.removeItem(key)
    } catch (error) {
      return false
    }

    return true
  }
}

export const simpleSessionStorage = new SimpleStorage('session')
export const simpleLocalStorage = new SimpleStorage('local')
